#include <InitrdFilesystem.h>
#include <FileDescriptor.h>
#include <MonitorDriver.h>
#include <RegularFile.h>

bool InitrdFilesystem::open(int impl, FileDescriptor *fd)
{
  return true;
}

bool InitrdFilesystem::close(int impl, FileDescriptor *fd)
{
  return true;
}

u32int InitrdFilesystem::read(int impl, u32int offset, u32int size,
                              u8int *buffer)
{
  InitrdFileHeader *header = (InitrdFileHeader*)impl;

  if (offset > header->length)
  {
    return 0;
  }
  
  if (offset+size > header->length)
  {
    size = header->length-offset;
  }
  Kernel::memcpy(buffer,
                 (u8int*) (header->offset+offset),
                 size );
  return size;
}

u32int InitrdFilesystem::write(int impl, u32int offset, u32int size,
                               u8int *buffer)
{
  PANIC("Error: attempted to write to initial ramdisk!");
}

u32int InitrdFilesystem::length(int impl)
{
  InitrdFileHeader *header = (InitrdFileHeader*)impl;
  
  return header->length;
}

InitrdFilesystem::InitrdFilesystem(Address addr)
{
  // Initialise the resource
  resource = new Resource((Callable*)this,"initrd");
  resource->add((Resource::Call2)&InitrdFilesystem::open,   FILE_OPEN);
  resource->add((Resource::Call2)&InitrdFilesystem::close,  FILE_CLOSE);
  resource->add((Resource::Call4)&InitrdFilesystem::read,   FILE_READ);
  resource->add((Resource::Call4)&InitrdFilesystem::write,  FILE_WRITE);
  resource->add((Resource::Call1)&InitrdFilesystem::length, FILE_LENGTH);
  
  // Initialise the main and file header pointers,
  // and populate the root directory.
  header = (InitrdHeader*) addr;
  fileHeaders = (InitrdFileHeader*) (addr+4);

  // Filesystem should be in user-mode memory.
  u32int fs_mem = (u32int)memoryManager.umalloc(sizeof(Filesystem));
  filesystem = new(fs_mem) Filesystem((char*)"initrdfs", NULL);
  
  // Make the root directory.
  Directory *root = new Directory(NULL, filesystem, resource->getId());
  root->addEntry(new Directory((char*)"dev", filesystem, resource->getId()));
  // TMP TMP TMP TMP TMP
  root->addEntry(new Directory((char*)"mnt", filesystem, resource->getId()));
  
  u32int lastAddr = 0;
  for (int i = 0; i < header->nFiles; i++)
  {
    // Edit the file header - currently it holds the file
    // offset relative to the start of the file. We want it
    // relative to the start of memory.
    fileHeaders[i].offset += addr;
    File *f = new RegularFile(fileHeaders[i].name, filesystem, resource->getId());
    u32int impl = (u32int)&fileHeaders[i];
    f->setImplDefined(impl);
    root->addEntry(f);
    if ( (fileHeaders[i].offset+fileHeaders[i].length) > lastAddr)
      lastAddr = (fileHeaders[i].offset+fileHeaders[i].length);
  }
  u32int *lastAddrPtr = (u32int*)lastAddr;
  ASSERT(*lastAddrPtr == 0xdeadbaba);
  filesystem->setRoot(root);
  
}

InitrdFilesystem::~InitrdFilesystem()
{
  // Go through the root directory, destroying any file objects
  // we find.
  Directory *root = filesystem->getRoot();
  u32int size = root->getNEntries();
  for (int i = 0; i < size; i++)
  {
    File *f = root->getEntryN(i);
    root->removeEntry(f->getName());
    delete f;
  }
  // Delete the root directory.
  delete root;
}
